<template>
  <div class="gz-editor">
    <div id="myDiagramDiv" style="height: 250px"></div>
    <xj-dialog :visible="editBdsDialogVisible" title="编辑表达式">
      <div class="edit-row">
        <el-link
            v-for="(value,key) in bdsNameObj"
            :key="key" type="primary" @click="insertToBds(`calc('`+key+`')`)">{{ key }}
        </el-link>
      </div>
      <div class="edit-row">
        <el-link
            v-for="(name,ysf) in ysfObj"
            :key="ysf" type="primary" @click="insertToBds(ysf)">{{ name }}
        </el-link>
      </div>
      <el-form :model="bdsForm" label-width="85px">
        <el-form-item label="表达式">
          <bds-input-box ref="bdsInput" v-model="bdsForm.bds"></bds-input-box>
        </el-form-item>
        <el-form-item label="运算类型">
          <el-radio-group v-model="bdsForm.yslx">
            <el-radio-button :label="null">普通运算</el-radio-button>
            <el-radio-button label="BLYS">遍历运算</el-radio-button>
          </el-radio-group>
<!--          <el-select v-model="bdsForm.yslx" style="width: 100%;">-->
<!--            <el-option :value="null" label="普通运算"></el-option>-->
<!--            <el-option label="遍历运算" value="BLYS"></el-option>-->
<!--          </el-select>-->
        </el-form-item>
        <el-form-item label="实体名称">
          <el-select v-model="bdsForm.bm"
                     :class="{red:isRed}"
                     value-key="bmc"
                     style="width: 100%;">
            <el-option
                v-for="bmObj in bmList"
                :key="bmObj.bmc"
                :label="bmObj.bzwmc"
                :value="bmObj">
              <div :style="{
                color :bmObj.first?'':'red'
              }">{{ bmObj.bzwmc }}
              </div>
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="">
          <div style="color:red;">遍历运算：遍历运算时可能需要选择【多】的实体</div>
          <div style="color:red;">红色表名：非表达式中的实体名称</div>
        </el-form-item>

      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="handleConfirmBtnClick(false)">取消</el-button>
        <el-button type="primary" @click="handleConfirmBtnClick(true)">确定</el-button>
      </div>
    </xj-dialog>
  </div>
</template>

<script>
import uuid from 'uuid/v4'
import XjDialog from "@/components/XjDialog";
import BdsInputBox from "@/components/BdsInputBox";

let make = go.GraphObject.make;


function getBdsmcList(bds) {
  if (!bds) return []
  let arr = bds.match(/(?<=calc\(\s*')(\w+)(?='\s*\))/g)
  if (!arr) return []
  return Array.from(new Set(arr.filter(item => item.indexOf("calc") < 0)))
}

export default {
  name: "GzEditor",
  components: {XjDialog, BdsInputBox},
  props: {
    gzbds: {
      required: true
    },
    bdsList: {
      type: Array,
      default: () => ([]),
      required: true,
    },
    bdsNameObj: {
      type: Object,
      default: () => ({}),
      required: true,
    }
  },
  data() {
    return {
      editBdsDialogVisible: false,
      myDiagram: null,
      ysfObj: {
        "&&": '并且',
        "||": '或者',
        ">": '大于',
        ">=": '大于等于',
        "<": '小于',
        "<=": '小于等于',
        "!=": '不等于',
        "==": '等于',
      },
      bdsForm: {
        bm: {bmc: ''}
      },
      node: null
    }
  },
  computed: {
    isRed() {
      let bm = this.bdsForm.bm
      if (!bm) return false
      let obj = this.bmList.find(item => item.bmc === bm.bmc)
      return obj ? !obj.first : false
    },
    bmList() {
      let arr = []
      let bdsmcList = getBdsmcList(this.bdsForm.bds)
      for (let resKey of bdsmcList) {
        let bdsNameObjElement = this.bdsNameObj[resKey];
        if (!bdsNameObjElement) continue
        bdsNameObjElement.forEach(item => {
          let bmc = item.zdmc.split(".")[0]
          let b = arr.find(_item => _item.bmc === bmc)
          if (!b) {
            arr.push({
              bmc,
              first: true,
              bzwmc: item.zdzwmc.split(".")[0]
            })
          }
        })
      }
      for (let resKey in this.bdsNameObj) {
        let bdsNameObjElement = this.bdsNameObj[resKey];
        if (!bdsNameObjElement) continue
        bdsNameObjElement.forEach(item => {
          let bmc = item.zdmc.split(".")[0]
          let b = arr.find(_item => _item.bmc === bmc)
          if (!b) {
            arr.push({
              bmc,
              first: false,
              bzwmc: item.zdzwmc.split(".")[0]
            })
          }
        })
      }
      return arr
    }
  },
  watch: {
    'bdsForm.bds'() {
      if (!this.bdsForm.bm.bmc) {
        let bmObj = this.bmList[0]
        if (bmObj) this.bdsForm.bm.bmc = bmObj.bmc
      }
    },
    gzbds() {
      this.initModel()
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    insertToBds(str) {
      // let bds = this.bdsForm.bds
      // if (bds === null) bds = ""
      // this.bdsForm.bds = (bds + ` ${str}`).trim()
      let bdsInput = this.$refs.bdsInput
      bdsInput.insert(str)
    },
    getGzbds() {
      let model = this.myDiagram.model
      let nodeDataArray = JSON.parse(JSON.stringify(model.nodeDataArray))
      let hasNoBds = nodeDataArray.some(item => !item.data.bds)
      if (hasNoBds) throw new Error("规则尚未编辑完成")
      let linkDataArray = JSON.parse(JSON.stringify(model.linkDataArray))
      for (let link of linkDataArray) {
        let from = nodeDataArray.find(item => item.key === link.from)
        if (!from.data.bds) {
          return this.$message({message: "存在空白节点", sjly: "error"})
        }
        let to = nodeDataArray.find(item => item.key === link.to)
        if (!to.data.bds) {
          return this.$message({message: "存在空白节点", sjly: "error"})
        }
        from.data[link.fromport].push(to.data)
        to.isChild = true
      }
      return nodeDataArray.find(item => !item.isChild).data
    },
    handleConfirmBtnClick(isConfirm) {
      if (isConfirm) {
        if (this.bdsForm.bm) {
          this.bdsForm.bmc = this.bdsForm.bm.bmc
        }
        this.node.data = JSON.parse(JSON.stringify(this.bdsForm))
        this.myDiagram.model.updateTargetBindings(this.node)
      }
      this.editBdsDialogVisible = false
      this.bdsForm = {bm: {}}
    },
    initModel() {
      let gzbds = JSON.parse(JSON.stringify(this.gzbds || {bds: ''}))
      this.myDiagram.model = make(go.GraphLinksModel, {
        linkFromPortIdProperty: "fromport",
        ...this.getModel([gzbds])
      });
    },
    getModel(arr, pnode, fromport) {
      let resobj = {nodeDataArray: [], linkDataArray: []};
      for (let obj of arr) {
        let node = this.getNode(obj)
        resobj.nodeDataArray.push(node);
        if (pnode) {
          let link = this.getLink(node, pnode, fromport)
          resobj.linkDataArray.push(link)
        }
        if (!obj) continue
        if (obj.yes) {
          let nodes = this.getModel(obj.yes, node, "yes")
          resobj.nodeDataArray.push(...nodes.nodeDataArray)
          resobj.linkDataArray.push(...nodes.linkDataArray)
        }
        obj.yes = []
        if (obj.no) {
          let nodes = this.getModel(obj.no, node, "no")
          resobj.nodeDataArray.push(...nodes.nodeDataArray)
          resobj.linkDataArray.push(...nodes.linkDataArray)
        }
        obj.no = []
      }
      return resobj
    },
    getLink(node, pnode, fromport) {
      return {
        category: 'auditedLineColor',
        from: pnode.key,
        fromport,
        to: node.key
      }
    },
    getNode(obj) {
      obj = obj || {
        bds: "",
        bmc: "",
        no: [],
        yes: [],
        yslx: null
      }
      return {
        key: uuid(),
        data: obj,
        category: "decision"
      }
    },
    objIsNull(obj) {
      for (const bdsmc in obj) {
        return false
      }
      return true
    },
    handleEditBtnClick(e, port) {
      if (this.objIsNull(this.bdsNameObj)) {
        return this.$message({message: '请先添加表达式', sjly: 'error'})
      }
      let node = port.part.data
      this.bdsForm = {
        ...node.data,
        bm: {
          bmc: node.data.bmc
        }
      }
      this.node = node
      this.editBdsDialogVisible = true
    },
    handleAddBtnClick(e, port) {
      let pnode = port.part.data
      if (!pnode.data.bds) return
      let node = this.getNode()
      let link = this.getLink(node, pnode, port.portId)
      let model = this.myDiagram.model
      let nodeDataArray = model.nodeDataArray
      let linkDataArray = model.linkDataArray
      nodeDataArray.push(node)
      let num = linkDataArray.length - 1
      if (port.portId === 'yes') {
        num = -1
      }
      for (let i = linkDataArray.length - 1; i >= 0; i--) {
        let _link = linkDataArray[i]
        if (_link.from === pnode.key && _link.fromport === port.portId) {
          num = i;
          break
        }
      }
      linkDataArray.splice(num + 1, 0, link)
      this.myDiagram.model = make(go.GraphLinksModel, {
        linkFromPortIdProperty: "fromport",
        nodeDataArray: nodeDataArray,
        linkDataArray: linkDataArray
      });
    },
    canDeleteSelection() {
      for (let it = this.myDiagram.selection.iterator; it.next();) {
        let node = it.value;
        if (!(node instanceof go.Node)) return false
        let cnode = node.findNodesOutOf()
        if (cnode.count) return false;
        let pnode = node.findNodesInto()
        return pnode.count;
      }
    },
    bdsToZw(bds) {
      if (!bds) {
        return "双击编辑表达式"
      }
      return bds
          .replace(/(\|\|)|(&&)/g, word => {
            let ysf = this.ysfObj[word]
            return "\n" + (ysf || word)
          })
          .replace(/calc\('(\w+)'\)/g, (word, bdsmc, c) => {
            let bdsObj = this.bdsList.find(item => item.bdsmc === bdsmc)
            return bdsObj ? bdsObj.bdsms : word
          })
    },
    init() {
      this.myDiagram = make(go.Diagram, "myDiagramDiv", {
        initialContentAlignment: go.Spot.Left,
        isReadOnly: true,
        allowMove: false,
        "animationManager.isEnabled": false,
        // "toolManager.mouseWheelBehavior": go.ToolManager.WheelNone,
        layout: make(go.TreeLayout)
      })

      this.myDiagram.commandHandler.canDeleteSelection = this.canDeleteSelection;

      let tooltipTemplate = make("ToolTip", make(go.TextBlock, {font: "8pt sans-serif", margin: 6},
          new go.Binding("text", "", data => this.bdsToZw(data.data.bds)))
      );

      this.myDiagram.nodeTemplateMap.add("decision",
          make(go.Node, "Auto", {
                toolTip: tooltipTemplate,
                doubleClick: this.handleEditBtnClick
              },
              make(go.Picture, {}, new go.Binding("source", "", () => {
                return require('@/assets/img/bg.svg')
              })),
              make(go.Panel, "Horizontal",
                  {
                    padding: 10
                  },
                  make(go.TextBlock, {font: "15px Roboto, sans-serif", margin: 5},
                      new go.Binding("text", "", data => data.data.bds || '双击编辑/修改'),
                      new go.Binding("stroke", "", data => {
                        if (data.data.bds) {
                          return 'black'
                        }
                        return 'red'
                      }),
                  ),
                  make(go.Panel, "Vertical",
                      make("Button",
                          {click: this.handleAddBtnClick, margin: 3},
                          make(go.Picture, {
                            height: 16,
                            width: 16,
                          }, new go.Binding("source", "", () => {
                            return require('@/assets/img/true.png')
                          })),
                          new go.Binding("portId", "", () => "yes"),
                          new go.Binding("visible", "", ({data}) => {
                            if (!data.bds) return false
                            let bdsmcList = data.bds.match(/bds\d+/g)
                            for (let bdsmc of bdsmcList) {
                              let bds = this.bdsList.find(item => item.bdsmc === bdsmc)
                              if (bds && bds.bdslx === "TS") {
                                return false
                              }
                            }
                            return true
                          }),
                      ),
                      make("Button",
                          new go.Binding("portId", "", () => "no"),
                          {click: this.handleAddBtnClick, margin: 3},
                          make(go.Picture, {
                            height: 16,
                            width: 16,
                          }, new go.Binding("source", "", () => {
                            return require('@/assets/img/false.png')
                          })),
                          new go.Binding("visible", "", ({data}) => {
                            if (!data.bds) return false
                            let bdsmcList = data.bds.match(/bds\d+/g)
                            for (let bdsmc of bdsmcList) {
                              let bds = this.bdsList.find(item => item.bdsmc === bdsmc)
                              if (bds && bds.bdslx === "TS") {
                                return false
                              }
                            }
                            return true
                          }),
                      )
                  )
              )
          )
      );

      this.myDiagram.linkTemplateMap.add("auditedLineColor",
          make(go.Link,
              {
                selectable: false
              },
              make(go.Shape, {stroke: 'orange', strokeWidth: 2}),
              make(go.Shape,
                  {
                    stroke: 'transparent',
                    strokeWidth: 0,
                    fromArrow: 'Standard',
                    toArrow: 'Standard',
                    fill: 'orange',
                    scale: 1.5
                  }
              )
          )
      );

      this.initModel()
    }
  }
}
</script>

<style lang="scss" scoped>
.gz-editor {
  .edit-row {
    text-align: center;
    margin: 5px 0;

    a {
      margin: 0 5px;
    }
  }

  ::v-deep .el-select.red {
    .el-input {
      .el-input__inner {
        color: red;
      }
    }
  }
}
</style>
